{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extend jupyterlab-lsp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How to add a new LSP feature?\n",
    "\n",
    "Features (as well as other parts of the frontend) reuse the\n",
    "[JupyterLab plugins system](https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html#plugins).\n",
    "Each plugin is a [TypeScript](https://www.typescriptlang.org/) package exporting\n",
    "one or more `JupyterFrontEndPlugin`s (see\n",
    "[the JupyterLab extesion developer tutorial](https://jupyterlab.readthedocs.io/en/stable/extension/extension_tutorial.html)\n",
    "for an overview).\n",
    "\n",
    "Each feature has to register itself with the `FeatureManager` (which is provided\n",
    "after requesting `ILSPFeatureManager` token from `@jupyterlab/lsp`) using\n",
    "`register(options: IFeatureOptions)` method.\n",
    "\n",
    "The feature specification should follow the `IFeature` interface as of\n",
    "JupyterLab 4.0, including:\n",
    "\n",
    "- `id`: unique identifier of the feature, we recommend\n",
    "  `@organization/project:feature` pattern\n",
    "- `capabilities`: an optional object defining the [client\n",
    "  capabilities][clientcapabilities] implemented by your feature,\n",
    "\n",
    "See JupyterLab\n",
    "[Extension Points >> LSP Features](https://jupyterlab.readthedocs.io/en/latest/extension/extension_points.html#lsp-features)\n",
    "documentation for more details.\n",
    "\n",
    "#### How to override the default implementation of a feature?\n",
    "\n",
    "You can specify a list of plugins implementing features which you want to\n",
    "disable in [`jupyterlab.disabledExtensions`][disabledextensions] stanza of\n",
    "`package.json`, for example:\n",
    "\n",
    "```json\n",
    "\"jupyterlab\": {\n",
    "  \"disabledExtensions\": [\"@jupyter-lsp/jupyterlab-lsp:hover\"]\n",
    "}\n",
    "```\n",
    "\n",
    "will disable the hover feature.\n",
    "\n",
    "[clientCapabilities]:\n",
    "https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#clientCapabilities]\n",
    "[disabledExtensions]:\n",
    "https://jupyterlab.readthedocs.io/en/latest/extension/extension_dev.html#disabling-other-extensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How to add a custom magic or foreign extractor?\n",
    "\n",
    "It is now possible to register custom code replacements using\n",
    "`ILSPCodeOverridesManager` token and to register custom foreign code extractors\n",
    "using `ILSPCodeExtractorsManager` token, however this API is considered\n",
    "provisional and subject to change.\n",
    "\n",
    "#### Future plans for transclusions handling\n",
    "\n",
    "We welcome pull requests enabling kernels to register their custom syntax/code\n",
    "transformations. The frontend API will remain available for the end-users who\n",
    "write their custom syntax modifications with actionable side-effects (e.g. a\n",
    "custom IPython magic which copies a variable from the host document to the\n",
    "embedded document)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How to add custom icons for the completer?\n",
    "\n",
    "1. Prepare the icons in the SVG format (we use 16 x 16 pixels, but you should be\n",
    "   fine with up to 24 x 24). You can load them for webpack in typescript using\n",
    "   imports if you include a `typings.d.ts` file with the following content:\n",
    "\n",
    "   ```typescript\n",
    "   declare module '*.svg' {\n",
    "     const script: string;\n",
    "     export default script;\n",
    "   }\n",
    "   ```\n",
    "\n",
    "   in your `src/`. You should probably keep the icons in your `style/`\n",
    "   directory.\n",
    "\n",
    "2. Prepare `CompletionKind` → `IconSvgString` mapping for the light (and\n",
    "   optionally dark) theme, implementing the `ICompletionIconSet` interface. We\n",
    "   have an additional `Kernel` completion kind that is used for completions\n",
    "   provided by kernel that had no recognizable type provided.\n",
    "\n",
    "3. Provide all other metadata required by the `ICompletionTheme` interface and\n",
    "   register it on `ILSPCompletionThemeManager` instance using `register_theme()`\n",
    "   method.\n",
    "\n",
    "4. Provide any additional CSS styling targeting the JupyterLab completer\n",
    "   elements inside of `.lsp-completer-theme-{id}`, e.g.\n",
    "   `.lsp-completer-theme-material .jp-Completer-icon svg` for the material\n",
    "   theme. Remember to include the styles by importing the in one of the source\n",
    "   files.\n",
    "\n",
    "For an example of a complete theme see\n",
    "[theme-vscode](https://github.com/jupyter-lsp/jupyterlab-lsp/tree/master/packages/theme-vscode)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Migrating to v5.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `IFeature` interface was moved to `@jupyterlab/lsp`\n",
    "  - `labIntegration` was removed,\n",
    "  - `editorIntegrationFactory` was removed in JupyterLab 4.0 and restored in\n",
    "    JupyterLab 4.1 as `extensionFactory` with new API\n",
    "    (`ILSPEditorExtensionFactory`),\n",
    "  - `supersedes` was removed; you can disable extensions using the JupyterLab\n",
    "    native `jupyterlab.disabledExtensions` stanza of `package.json`.\n",
    "- `ILSPCompletionThemeManager`:\n",
    "  - `register_theme()` was renamed to `registerTheme()`\n",
    "  - all other methods were renamed to follow camelCase convention"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extend jupyter-lsp\n",
    "\n",
    "### Language Server Specs\n",
    "\n",
    "Language Server Specs can be [configured](./Configuring.html) by Jupyter users,\n",
    "or distributed by third parties as python or JSON files. Since we'd like to see\n",
    "as many Language Servers work out of the box as possible, consider\n",
    "[contributing a spec](./Contributing.html#specs), if it works well for you!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Message Listeners\n",
    "\n",
    "Message listeners may choose to receive LSP messages immediately after being\n",
    "received from the client (e.g. `jupyterlab-lsp`) or a language server. All\n",
    "listeners of a message are scheduled concurrently, and the message is passed\n",
    "along **once all listeners return** (or fail). This allows listeners to, for\n",
    "example, modify files on disk before the language server reads them.\n",
    "\n",
    "If a listener is going to perform an expensive activity that _shouldn't_ block\n",
    "delivery of a message, a non-blocking technique like\n",
    "[IOLoop.add_callback][add_callback] and/or a\n",
    "[queue](https://www.tornadoweb.org/en/stable/queues.html) should be used.\n",
    "\n",
    "[add_callback]:\n",
    "  https://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_callback"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Add a Listener with `entry_points`\n",
    "\n",
    "Listeners can be added via [entry_points][] by a package installed in the same\n",
    "environment as `notebook`:\n",
    "\n",
    "```toml\n",
    "## setup.cfg\n",
    "\n",
    "[options.entry_points]\n",
    "jupyter_lsp_listener_all_v1 =\n",
    "  some-unique-name = some.module:some_function\n",
    "jupyter_lsp_listener_client_v1 =\n",
    "  some-other-unique-name = some.module:some_other_function\n",
    "jupyter_lsp_listener_server_v1 =\n",
    "  yet-another-unique-name = some.module:yet_another_function\n",
    "```\n",
    "\n",
    "At present, the entry point names generally have no impact on functionality\n",
    "aside from logging in the event of an error on import.\n",
    "\n",
    "[entry_points]: https://packaging.python.org/specifications/entry-points/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Add a Listener with Jupyter Configuration\n",
    "\n",
    "Listeners can be added via `traitlets` configuration, e.g.\n",
    "\n",
    "```yaml\n",
    "## jupyter_server_config.jsons\n",
    "{\n",
    "  'LanguageServerManager':\n",
    "    {\n",
    "      'all_listeners': ['some.module.some_function'],\n",
    "      'client_listeners': ['some.module.some_other_function'],\n",
    "      'server_listeners': ['some.module.yet_another_function']\n",
    "    }\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Add a listener with the Python API\n",
    "\n",
    "`lsp_message_listener` can be used as a decorator, accessed as part of a\n",
    "`serverextension`.\n",
    "\n",
    "This listener receives _all_ messages from the client and server, and prints\n",
    "them out.\n",
    "\n",
    "```python\n",
    "from jupyter_lsp import lsp_message_listener\n",
    "\n",
    "def load_jupyter_server_extension(nbapp):\n",
    "\n",
    "    @lsp_message_listener(\"all\")\n",
    "    async def my_listener(scope, message, language_server, manager):\n",
    "        print(\"received a {} {} message from {}\".format(\n",
    "          scope, message[\"method\"], language_server\n",
    "        ))\n",
    "```\n",
    "\n",
    "`scope` is one of `client`, `server` or `all`, and is required."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listener options\n",
    "\n",
    "Fine-grained controls are available as part of the Python API. Pass these as\n",
    "named arguments to `lsp_message_listener`.\n",
    "\n",
    "- `language_server`: a regular expression of language servers\n",
    "- `method`: a regular expression of LSP JSON-RPC method names"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
